#include "Gradient.h"

namespace liba
{
namespace lib3d
{
namespace node
{
	
converter::EnumPair<char, Gradient::Direction> Gradient::DirectionNames[] = 
{
	converter::MakeEnumPair("LEFT",  Gradient::LEFTGRADIENT),
	converter::MakeEnumPair("RIGHT", Gradient::RIGHTGRADIENT),
	converter::MakeEnumPair("UP",    Gradient::UPGRADIENT),
	converter::MakeEnumPair("DOWN",  Gradient::DOWNGRADIENT),
	0
};
	
converter::EnumPair<char, Gradient::AlignmentType> Gradient::AlignmentTypeNames[] = 
{
	converter::MakeEnumPair("CENTER", Gradient::CENTER),
	converter::MakeEnumPair("LEFT",   Gradient::LEFT),
	converter::MakeEnumPair("RIGHT",  Gradient::RIGHT),
	converter::MakeEnumPair("TOP",    Gradient::TOP),
	converter::MakeEnumPair("BOTTOM", Gradient::BOTTOM),
	0
};
	
const Atom Gradient::node_name("Gradient");

Gradient::Gradient(const Atom & name, Node * parent) : Node(name, parent), _needUpdate(false), _startColor(1.0f, 1.0f, 1.0f), _endColor(0.0f, 0.0f, 0.0f),
													   _startTransparency(1.0f), _endTransparency(0.0f), _width(0.0f), _height(0.0f), _alignX(LEFT), _alignY(TOP),
													   _direction(LEFTGRADIENT)
{
	node_type = node_name;
	hard::Manager::add_dependent(this);
}

Gradient::~Gradient()
{
	hard::Manager::remove_dependent(this);
}

Gradient * Gradient::Clone()
{
	Gradient * newGradient = new Gradient(*this);
	hard::Manager::add_dependent(newGradient);
	return newGradient;
}

void Gradient::on_add_hardware(hard::Manager::hardware_type * ha)
{
	hard_vector.push_back(SweetPair(ha, ha->create_vertex_buffer()));
}

void Gradient::RenderToList(hard::Hardware * hardware, hard::RenderList * render_list)
{
	// getting object and check if it valid
	hard::VertexBuffer * vb = find_data(hardware);
	if(!vb->is_valid() || _needUpdate)
		rebuild_gradient_plane();
	
	// fill render model
	hard::RenderModel render_model;
	render_model.world                 = GetGlobalMatrix();
	render_model.vb                    = vb;
	render_model.primitive_start       = 0;
	render_model.primitive_count       = 4;
	render_model.ib                    = NULL;
	render_model.vertex_start          = 0;
	render_model.vertex_count          = 12;
	render_model.material.transparency = 0.001f;
	
	// add to render list
	render_list->models.push_back(render_model);
}

void Gradient::rebuild_gradient_plane()
{
	// updated now
	_needUpdate = false;
	
	// vertex buffer's data
	SimpleArray<float> positions(36);
	SimpleArray<float> colors(48);
	
	// data pointers
	float * positionsData = positions.data();
	float * colorsData    = colors.data();
	
	// setup plane coordinates
	float dx = _width;
	float x  = get_x_with_align(dx);
	float dy = _height;
	float y  = get_y_with_align(dy);
	
	*positionsData++ = x;
	*positionsData++ = y;
	*positionsData++ = 0.0f;

	*positionsData++ = x;
	*positionsData++ = y + dy;
	*positionsData++ = 0.0f;

	*positionsData++ = x + dx;
	*positionsData++ = y;
	*positionsData++ = 0.0f;

	*positionsData++ = x + dx;
	*positionsData++ = y;
	*positionsData++ = 0.0f;

	*positionsData++ = x;
	*positionsData++ = y + dy;
	*positionsData++ = 0.0f;

	*positionsData++ = x + dx;
	*positionsData++ = y + dy;
	*positionsData++ = 0.0f;

	*positionsData++ = x;
	*positionsData++ = y;
	*positionsData++ = 0.0f;
	
	*positionsData++ = x + dx;
	*positionsData++ = y;
	*positionsData++ = 0.0f;
	
	*positionsData++ = x;
	*positionsData++ = y + dy;
	*positionsData++ = 0.0f;
	
	*positionsData++ = x + dx;
	*positionsData++ = y;
	*positionsData++ = 0.0f;
	
	*positionsData++ = x + dx;
	*positionsData++ = y + dy;
	*positionsData++ = 0.0f;
	
	*positionsData++ = x;
	*positionsData++ = y + dy;
	*positionsData++ = 0.0f;
	
	// setup gradient
	switch(_direction)
	{
		case RIGHTGRADIENT:
		{
			// 1
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
			
			// 2
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
			
			// 3
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
			
			// 3
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
			
			// 2
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
			
			// 4
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
			
			// 1
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
			
			// 3
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
			
			// 2
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
			
			// 3
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
			
			// 4
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
			
			// 2
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
		}
		break;
			
		case LEFTGRADIENT:
		{
			// 1
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
			
			// 2
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
			
			// 3
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
			
			// 3
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
			
			// 2
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
			
			// 4
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
			
			// 1
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
			
			// 3
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
			
			// 2
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
			
			// 3
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
			
			// 4
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
			
			// 2
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
		}
		break;
			
		case UPGRADIENT:
		{
			// 1
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
			
			// 2
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
			
			// 3
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
			
			// 3
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
			
			// 2
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
			
			// 4
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
			
			// 1
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
			
			// 3
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
			
			// 2
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
			
			// 3
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
			
			// 4
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
			
			// 2
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
		}
		break;
			
		case DOWNGRADIENT:
		{
			// 1
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
			
			// 2
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
			
			// 3
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
			
			// 3
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
			
			// 2
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
			
			// 4
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
			
			// 1
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
			
			// 3
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
			
			// 2
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
			
			// 3
			*colorsData++ = _endColor.r;
			*colorsData++ = _endColor.g;
			*colorsData++ = _endColor.b;
			*colorsData++ = 1.0f - _endTransparency;
			
			// 4
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
			
			// 2
			*colorsData++ = _startColor.r;
			*colorsData++ = _startColor.g;
			*colorsData++ = _startColor.b;
			*colorsData++ = 1.0f - _startTransparency;
		}
		break;
	}
	
	// update vertex buffer
	SimpleArray<float> emptyFloat;
	HardVector::iterator it = hard_vector.begin();
	for(; it != hard_vector.end(); ++it)
	{
		if(!it->second->update(positions, emptyFloat, colors, emptyFloat, emptyFloat))
			THROW(Gluck(__FUNCTION__, "vb->update() failed"));
	}
}

bool Gradient::on_attribute(xml::Provider * provider, const std::string & name, const std::string & value)
{
	if(name == "Width")
		return converter::convert(value, &_width);
	
	if(name == "Height")
		return converter::convert(value, &_height);
	
	if(name == "AlignX")
		return converter::convert(value, &_alignX);
	
	if(name == "AlignY")
		return converter::convert(value, &_alignY);
	
	if(name == "Direction")
		return converter::convert(value, &_direction);
	
	if(name == "StartColor")
		return converter::convert(value, &_startColor);
	
	if(name == "EndColor")
		return converter::convert(value, &_endColor);
	
	if(name == "StartTransparency")
		return converter::convert(value, &_startTransparency);
	
	if(name == "EndTransparency")
		return converter::convert(value, &_endTransparency);
	
	return Node::on_attribute(provider, name, value);
}

void Gradient::set_colors(const Color & startColor, const Color & endColor, float startTransparency, float endTransparency)
{
	_startColor        = startColor;
	_endColor          = endColor;
	_startTransparency = startTransparency;
	_endTransparency   = endTransparency;
	
	_needUpdate        = true;
}

void Gradient::set_size(float width, float height)
{
	_width      = width;
	_height     = height;
	
	_needUpdate = true;
}

Color Gradient::get_start_color() const
{
	return _startColor;
}

Color Gradient::get_end_color() const
{
	return _endColor;
}

float Gradient::get_start_transparency() const
{
	return _startTransparency;
}

float Gradient::get_end_transparency() const
{
	return _endTransparency;
}

float Gradient::get_width() const
{
	return _width;
}

float Gradient::get_height() const
{
	return _height;
}
	
float Gradient::get_x_with_align(float dx)
{
	switch(_alignX)
	{
		case LEFT:
			return 0;
		case RIGHT:
			return -dx;
		default:
			return -dx / 2;
	}
}

float Gradient::get_y_with_align(float dy)
{
	switch(_alignY)
	{
		case BOTTOM:
			return 0;
		case TOP:
			return -dy;
		default:
			return -dy / 2;
	}
}

}
}
}